package com.xhj.mybatis.session;

import com.xhj.mybatis.binding.MapperRegistry;
import com.xhj.mybatis.cache.Cache;
import com.xhj.mybatis.cache.decorators.FifoCache;
import com.xhj.mybatis.cache.decorators.LruCache;
import com.xhj.mybatis.cache.impl.PerpetualCache;
import com.xhj.mybatis.datasource.druid.DruidDataSourceFactory;
import com.xhj.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.xhj.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.xhj.mybatis.executor.CachingExecutor;
import com.xhj.mybatis.executor.Executor;
import com.xhj.mybatis.executor.SimpleExecutor;
import com.xhj.mybatis.executor.keygen.KeyGenerator;
import com.xhj.mybatis.executor.parameter.ParameterHandler;
import com.xhj.mybatis.executor.resultset.DefaultResultSetHandler;
import com.xhj.mybatis.executor.resultset.ResultSetHandler;
import com.xhj.mybatis.executor.statement.PreparedStatementHandler;
import com.xhj.mybatis.executor.statement.StatementHandler;
import com.xhj.mybatis.logging.slf4j.Slf4jImpl;
import com.xhj.mybatis.mapping.*;
import com.xhj.mybatis.plugin.Interceptor;
import com.xhj.mybatis.plugin.InterceptorChain;
import com.xhj.mybatis.reflection.MetaObject;
import com.xhj.mybatis.reflection.ReflectorFactory;
import com.xhj.mybatis.reflection.factory.DefaultObjectFactory;
import com.xhj.mybatis.reflection.factory.ObjectFactory;
import com.xhj.mybatis.reflection.wrapper.DefaultObjectWrapperFactory;
import com.xhj.mybatis.reflection.wrapper.ObjectWrapperFactory;
import com.xhj.mybatis.scripting.LanguageDriver;
import com.xhj.mybatis.scripting.LanguageDriverRegistry;
import com.xhj.mybatis.scripting.xmltags.XMLLanguageDriver;
import com.xhj.mybatis.transaction.Transaction;
import com.xhj.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.xhj.mybatis.type.JdbcType;
import com.xhj.mybatis.type.TypeAliasRegistry;
import com.xhj.mybatis.type.TypeHandlerRegistry;

import java.util.*;
import java.util.function.BiFunction;

/**
 * Configuration
 *
 * @author XJks
 * @description 全局配置类，存放核心配置文件解析出来的内容
 */
public class Configuration {

    /**
     * 环境配置
     */
    protected Environment environment;

    /**
     * 映射器注册中心
     */
    protected MapperRegistry mapperRegistry = new MapperRegistry(this);

    /**
     * 存储已解析的 MappedStatement 对象
     */
    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection").conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and " + targetValue.getResource());

    /**
     * 存储已解析的 Cache 对象
     */
    protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");

    /**
     * 类型别名注册中心
     */
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();

    /**
     * 类型处理器注册中心
     */
    protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);

    /**
     * 语言驱动注册中心
     */
    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

    /**
     * 反射工厂
     */
    protected ReflectorFactory reflectorFactory = ReflectorFactory.GLOBAL_INSTANCE;

    /**
     * 对象工厂（负责创建所有的对象实例）
     */
    protected ObjectFactory objectFactory = new DefaultObjectFactory();

    /**
     * 对象包装工厂（负责创建对象的包装器）
     */
    protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

    /**
     * 拦截器链
     */
    protected final InterceptorChain interceptorChain = new InterceptorChain();

    /**
     * 是否压缩 SQL 语句中的多余空白字符（如换行、制表符等），默认为 false
     */
    protected boolean shrinkWhitespacesInSql = false;

    /**
     * 是否使用列标签（Column Label）代替列名（Column Name），默认为 true
     */
    protected boolean useColumnLabel = true;

    /**
     * 是否开启下划线到驼峰命名的映射规则，默认为 false。
     */
    protected boolean mapUnderscoreToCamelCase = false;

    /**
     * 是否在对象属性为 null 时依然调用 setter 方法
     * 作用：如果为 true，当数据库查询结果为 null 时，MyBatis 依然会调用 JavaBean 的 setter 方法（将属性设置为 null）
     * 场景：有些业务需要明确地将属性设置为 null，而不是跳过 setter 调用
     * 默认值：未初始化，默认为 true
     */
    protected boolean callSettersOnNulls = true;

    /**
     * 是否使用实际的参数名称，默认为 true
     */
    protected boolean useActualParamName = true;

    /**
     * 是否启用 JDBC 的自动生成主键功能（useGeneratedKeys）。
     * 说明：
     * - 如果设置为 true，MyBatis 在插入数据时会尝试使用 JDBC 的 getGeneratedKeys 方法自动获取数据库生成的主键（如自增 ID）。
     * - 如果设置为 false，则不会自动获取主键，需要手动处理。
     * - 适用于数据库支持自动生成主键的场景（如 MySQL 的 AUTO_INCREMENT）。
     * - 该选项可以通过配置文件或代码设置，影响所有插入操作的主键处理方式。
     */
    protected boolean useGeneratedKeys;

    /**
     * 用于指定当参数值为 null 时，JDBC 应该使用的默认 JdbcType，默认为 JdbcType.OTHER
     */
    protected JdbcType jdbcTypeForNull = JdbcType.OTHER;

    /**
     * 本地缓存作用域，默认为 SESSION
     */
    protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;

    /**
     * 是否全局启用缓存，默认为 true
     */
    protected boolean cacheEnabled = true;

    /**
     * 用来标识当前 Configuration 对应的数据库厂商 ID（可以为 null）。
     * 说明：
     * - 目的：在同一套映射（Mapper）中，可能会为不同数据库提供不同的 SQL 实现（例如针对 MySQL 或 Oracle 的差异）。
     * MyBatis 可以根据这个 `databaseId` 来选择匹配的 `<select|insert|update|delete databaseId="...">` 或注解中的提供者方法。
     * - 常见取值：`"mysql"`、`"oracle"`、`"postgresql"` 等（项目中通常由 `VendorDatabaseIdProvider` 检测并设置）。
     * - 为何重要：当有数据库差异（语法、函数或分页等）时，通过 `databaseId` 能让配置文件按数据库类型生效，避免手动切换代码。
     * - 使用行为：若为 `null`，表示未指定厂商 ID，MyBatis 会优先选择不带 `databaseId` 的语句或按默认规则处理。
     * - 注意事项：该字段仅是一个标识字符串，本身不负责检测数据库类型；通常由外部提供者在 Configuration 初始化时设置。
     */
    protected String databaseId;

    /**
     * 存储已解析的 ResultMap 对象
     */
    protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");

    /**
     * 存储已解析的 KeyGenerator 对象
     */
    protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");

    /**
     * 记录已加载的资源，防止重复加载
     */
    protected final Set<String> loadedResources = new HashSet<>();

    public Configuration() {
        // 注册事务管理器工厂的别名
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        // 注册数据源工厂的别名
        typeAliasRegistry.registerAlias("DRUID", DruidDataSourceFactory.class);
        typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
        typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
        // 注册缓存实现的别名
        typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
        typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
        typeAliasRegistry.registerAlias("LRU", LruCache.class);
        // 注册数据库厂商 ID 提供者的别名
        typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
        typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
        // 注册语言驱动的别名
        typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
        // 注册日志实现的别名
        typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
        // 设置默认的语言驱动
        languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    }

    public void addMappers(String packageName) {
        mapperRegistry.addMappers(packageName);
    }

    public <T> void addMapper(Class<T> type) {
        mapperRegistry.addMapper(type);
    }

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
    }

    public boolean hasMapper(Class<?> type) {
        return mapperRegistry.hasMapper(type);
    }

    public boolean hasStatement(String statementName) {
        return mappedStatements.containsKey(statementName);
    }

    public void addResultMap(ResultMap rm) {
        resultMaps.put(rm.getId(), rm);
    }

    public ResultMap getResultMap(String id) {
        return resultMaps.get(id);
    }

    public boolean hasResultMap(String id) {
        return resultMaps.containsKey(id);
    }

    public void addMappedStatement(MappedStatement ms) {
        mappedStatements.put(ms.getId(), ms);
    }

    public MappedStatement getMappedStatement(String id) {
        return mappedStatements.get(id);
    }

    public TypeAliasRegistry getTypeAliasRegistry() {
        return typeAliasRegistry;
    }

    public Environment getEnvironment() {
        return environment;
    }

    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    public ReflectorFactory getReflectorFactory() {
        return reflectorFactory;
    }

    public void setReflectorFactory(ReflectorFactory reflectorFactory) {
        this.reflectorFactory = reflectorFactory;
    }

    public ObjectFactory getObjectFactory() {
        return objectFactory;
    }

    public void setObjectFactory(ObjectFactory objectFactory) {
        this.objectFactory = objectFactory;
    }

    public ObjectWrapperFactory getObjectWrapperFactory() {
        return objectWrapperFactory;
    }

    public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
        this.objectWrapperFactory = objectWrapperFactory;
    }

    public TypeHandlerRegistry getTypeHandlerRegistry() {
        return typeHandlerRegistry;
    }

    public LanguageDriverRegistry getLanguageRegistry() {
        return languageRegistry;
    }

    public void addInterceptor(Interceptor interceptor) {
        interceptorChain.addInterceptor(interceptor);
    }

    public List<Interceptor> getInterceptors() {
        return interceptorChain.getInterceptors();
    }

    public LocalCacheScope getLocalCacheScope() {
        return localCacheScope;
    }

    public void setLocalCacheScope(LocalCacheScope localCacheScope) {
        this.localCacheScope = localCacheScope;
    }

    public boolean isCacheEnabled() {
        return cacheEnabled;
    }

    public void setCacheEnabled(boolean cacheEnabled) {
        this.cacheEnabled = cacheEnabled;
    }

    public boolean isShrinkWhitespacesInSql() {
        return shrinkWhitespacesInSql;
    }

    public void setShrinkWhitespacesInSql(boolean shrinkWhitespacesInSql) {
        this.shrinkWhitespacesInSql = shrinkWhitespacesInSql;
    }

    public boolean isUseColumnLabel() {
        return useColumnLabel;
    }

    public void setUseColumnLabel(boolean useColumnLabel) {
        this.useColumnLabel = useColumnLabel;
    }

    public boolean isMapUnderscoreToCamelCase() {
        return mapUnderscoreToCamelCase;
    }

    public void setMapUnderscoreToCamelCase(boolean mapUnderscoreToCamelCase) {
        this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase;
    }

    public boolean isCallSettersOnNulls() {
        return callSettersOnNulls;
    }

    public void setCallSettersOnNulls(boolean callSettersOnNulls) {
        this.callSettersOnNulls = callSettersOnNulls;
    }

    public boolean isUseActualParamName() {
        return useActualParamName;
    }

    public void setUseActualParamName(boolean useActualParamName) {
        this.useActualParamName = useActualParamName;
    }

    public boolean isUseGeneratedKeys() {
        return useGeneratedKeys;
    }

    public void setUseGeneratedKeys(boolean useGeneratedKeys) {
        this.useGeneratedKeys = useGeneratedKeys;
    }

    public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
        keyGenerators.put(id, keyGenerator);
    }

    public KeyGenerator getKeyGenerator(String id) {
        return keyGenerators.get(id);
    }

    public boolean hasKeyGenerator(String id) {
        return keyGenerators.containsKey(id);
    }

    public void addCache(Cache cache) {
        caches.put(cache.getId(), cache);
    }

    public Cache getCache(String id) {
        return caches.get(id);
    }

    public JdbcType getJdbcTypeForNull() {
        return jdbcTypeForNull;
    }

    public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) {
        this.jdbcTypeForNull = jdbcTypeForNull;
    }

    public String getDatabaseId() {
        return databaseId;
    }

    public void setDatabaseId(String databaseId) {
        this.databaseId = databaseId;
    }

    public boolean isResourceLoaded(String resource) {
        return loadedResources.contains(resource);
    }

    public void addLoadedResource(String resource) {
        loadedResources.add(resource);
    }


    /**
     * 创建一个新的 MetaObject 实例，用于封装和操作指定的对象
     *
     * @param object
     * @return
     */
    public MetaObject newMetaObject(Object object) {
        return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }

    /**
     * 获取默认的 LanguageDriver（语言驱动器）实例。
     *
     * @return 返回默认的 LanguageDriver 实例
     */
    public LanguageDriver getDefaultScriptingLanguageInstance() {
        return languageRegistry.getDefaultDriver();
    }

    /**
     * 获取指定类型的 LanguageDriver（语言驱动器）实例。
     *
     * @param langClass 语言驱动器的类（Class对象），比如 XMLLanguageDriver.class
     * @return 返回对应类型的 LanguageDriver 实例
     */
    public LanguageDriver getLanguageDriver(Class<? extends LanguageDriver> langClass) {
        // 如果传入的类为 null，则返回默认的语言驱动器
        if (langClass == null) {
            return languageRegistry.getDefaultDriver();
        }
        // 将传入的语言驱动器类注册到 languageRegistry（如果之前没注册过）
        languageRegistry.register(langClass);
        // 从 languageRegistry 获取并返回对应类型的语言驱动器实例
        return languageRegistry.getDriver(langClass);
    }

    /**
     * 创建一个新的 ParameterHandler 实例，用于处理 SQL 参数的设置
     *
     * @param mappedStatement 映射语句
     * @param parameterObject 参数对象
     * @param boundSql        绑定的 SQL 语句
     * @return 参数处理器
     */
    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        // 通过 MappedStatement 的语言驱动器创建参数处理器
        ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
        // 应用所有拦截器（插件），对参数处理器进行增强
        parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
        // 返回最终的参数处理器
        return parameterHandler;
    }

    /**
     * 创建一个新的 ResultSetHandler 实例，用于处理 SQL 查询结果集
     *
     * @param executor         执行器
     * @param mappedStatement  映射语句
     * @param parameterHandler 参数处理器
     * @param resultHandler    结果处理器
     * @param boundSql         绑定的 SQL 语句
     * @param rowBounds        分页参数
     * @return 结果集处理器
     */
    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler<?> resultHandler, BoundSql boundSql, RowBounds rowBounds) {
        // 创建默认的结果集处理器
        ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
        // TODO:应用所有拦截器（插件），对结果集处理器进行增强
        resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
        // 返回最终的结果集处理器
        return resultSetHandler;
    }

    /**
     * 创建一个新的 StatementHandler 实例，用于处理 SQL 语句的执行
     *
     * @param executor        执行器
     * @param mappedStatement 映射语句
     * @param parameterObject 参数对象
     * @param rowBounds       分页参数
     * @param resultHandler   结果处理器
     * @param boundSql        绑定的 SQL 语句
     * @return 语句处理器
     */
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        // todo:源码这里会创建路由语句处理器，根据不同类型的 SQL 语句选择合适的处理器，这里简化为直接创建预编译语句处理器
        StatementHandler statementHandler = new PreparedStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        // TODO:应用所有拦截器（插件），对语句处理器进行增强
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        // 返回最终的语句处理器
        return statementHandler;
    }

    /**
     * 创建一个新的 Executor 实例（执行器），用于执行数据库操作（使用默认类型）
     *
     * @param transaction 事务对象
     * @return 执行器
     */
    public Executor newExecutor(Transaction transaction) {
        // todo:源码这里会调用重载方法，根据不同 ExecutorType 创建不同类型的执行器，这里简化为直接创建简单执行器
        Executor executor = new SimpleExecutor(this, transaction);
        // 如果开启了二级缓存，则用缓存执行器包装
        if (cacheEnabled) {
            executor = new CachingExecutor(executor);
        }
        // TODO:应用所有拦截器（插件），对执行器进行增强
        executor = (Executor) interceptorChain.pluginAll(executor);
        // 返回最终的执行器
        return executor;
    }

    /**
     * StrictMap 是一个自定义的 Map 实现，继承自 HashMap<String, V>，用于在 MyBatis 配置中存储带有严格校验的键值对
     *
     * @param <V> 值的类型
     */
    public static class StrictMap<V> extends HashMap<String, V> {

        /**
         * 序列化ID，保证序列化兼容性
         */
        private static final long serialVersionUID = 1L;

        /**
         * 用于标识这个 Map 的名字，便于报错时提示
         */
        private final String name;

        /**
         * 用于自定义冲突时的错误信息生成函数
         */
        private BiFunction<V, V, String> conflictMessageProducer;

        /**
         * 构造方法，指定名字、初始容量和负载因子
         *
         * @param name            map 名字
         * @param initialCapacity 初始容量
         * @param loadFactor      负载因子
         */
        public StrictMap(String name, int initialCapacity, float loadFactor) {
            super(initialCapacity, loadFactor);
            this.name = name;
        }

        /**
         * 构造方法，指定名字和初始容量
         *
         * @param name            map 名字
         * @param initialCapacity 初始容量
         */
        public StrictMap(String name, int initialCapacity) {
            super(initialCapacity);
            this.name = name;
        }

        /**
         * 构造方法，只指定名字
         *
         * @param name map 名字
         */
        public StrictMap(String name) {
            super();
            this.name = name;
        }

        /**
         * 构造方法，指定名字和已有的 Map
         *
         * @param name map 名字
         * @param m    已有的 Map
         */
        public StrictMap(String name, Map<String, ? extends V> m) {
            super(m);
            this.name = name;
        }

        /**
         * 设置冲突时的错误信息生成函数
         *
         * @param conflictMessageProducer 冲突时生成错误信息的函数（参数为已存在的值和新值）
         * @return 返回自身，方便链式调用
         */
        public StrictMap<V> conflictMessageProducer(BiFunction<V, V, String> conflictMessageProducer) {
            this.conflictMessageProducer = conflictMessageProducer;
            return this;
        }

        @Override
        public V put(String key, V value) {
            // 如果 key 已经存在，抛出异常，提示冲突
            if (containsKey(key)) {
                throw new IllegalArgumentException(name + " already contains value for " + key
                        + (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
            }
            // 如果 key 包含点号（.），说明是带命名空间的 key
            if (key.contains(".")) {
                final String shortKey = getShortName(key); // 取最后一段作为短 key
                if (super.get(shortKey) == null) {
                    // 如果短 key 没有被占用，则用短 key 存一份
                    super.put(shortKey, value);
                } else {
                    // 如果短 key 已经被占用，则用 Ambiguity 占位，表示歧义
                    super.put(shortKey, (V) new Ambiguity(shortKey));
                }
            }
            // 最后用完整 key 存储
            return super.put(key, value);
        }

        @Override
        public V get(Object key) {
            V value = super.get(key);
            // 如果没有找到，抛出异常
            if (value == null) {
                throw new IllegalArgumentException(name + " does not contain value for " + key);
            }
            // 如果查到的是 Ambiguity，说明短 key 有歧义，抛出异常提示
            if (value instanceof Ambiguity) {
                throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
                        + " (try using the full name including the namespace, or rename one of the entries)");
            }
            return value;
        }

        /**
         * 内部类，用于标记短 key 有歧义的情况
         */
        protected static class Ambiguity {

            /**
             * 歧义的主题（短 key）
             */
            private final String subject;

            public Ambiguity(String subject) {
                this.subject = subject;
            }

            public String getSubject() {
                return subject;
            }
        }

        /**
         * 获取 key 的短名（去掉命名空间，只取最后一段）
         *
         * @param key 完整的 key
         * @return 短名
         */
        private String getShortName(String key) {
            final String[] keyParts = key.split("\\.");
            return keyParts[keyParts.length - 1];
        }
    }
}
